package com.heima.article.service.impl;
import java.util.*;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.*;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.article.ArticleFeign;
import com.heima.feigns.behavior.BehaviorFeign;
import com.heima.feigns.user.UserFeign;
import com.heima.model.article.dto.ArticleDto;
import com.heima.model.article.dto.ArticleHomeDto;
import com.heima.model.article.dto.ArticleInfoDto;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojo.*;
import com.heima.model.article.vo.HotArticleVo;
import com.heima.model.behavior.dto.ApArticleRelationDto;
import com.heima.model.behavior.pojo.ApBehaviorEntry;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.AppThreadLocalUtils;
import com.heima.model.user.pojo.ApUser;
import com.heima.model.user.pojo.ApUserFollow;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {


    @Autowired
    AuthorMapper authorMapper;

    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    ApArticleContentMapper apArticleContentMapper;
    /**
     * 保存三表的信息
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveApArticle(ArticleDto articleDto) {

        //检查参数

        //将dto转为article对象
        ApArticle apArticle = new ApArticle();


        BeanUtils.copyProperties(articleDto,apArticle);//拷贝属性

        //根据作者名称查询作者id
        //文章表中特有的字段

        ApAuthor apAuthor = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, articleDto.getAuthorName()));
        if (apAuthor==null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"作者信息不存在");
        }

        apArticle.setAuthorId(Long.valueOf(apAuthor.getId()));
        //设置文章的作者的id


        //判断文章的id  是否存在  存在  即修改   删除旧的配置和内容

        //不存在   就保存

        if (apArticle.getId()==null){
            //如果id为空   保存文章
            //设置默认值
            apArticle.setLikes(0);
            apArticle.setCollection(0);
            apArticle.setComment(0);
            apArticle.setViews(0);
            save(apArticle);
        }else {
            //如果id不为空,修改文章

            updateById(apArticle);
            //删文章内容
             apArticleConfigMapper.delete(Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, apArticle.getId()));
             apArticleContentMapper.delete(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, apArticle.getId()));

            //删文章配置
        }
        //统一保存  配置信息  保存  文章的内容  另外两张表

        ApArticleConfig apArticleConfig = new ApArticleConfig();
        apArticleConfig.setArticleId(apArticle.getId());
        apArticleConfig.setIsComment(true);//可以评论
        apArticleConfig.setIsForward(true);//可以转发
        apArticleConfig.setIsDown(false);//上架
        apArticleConfig.setIsDelete(false);//未删除
        apArticleConfigMapper.insert(apArticleConfig);


        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setArticleId(apArticle.getId());
        apArticleContent.setContent(articleDto.getContent());
        apArticleContentMapper.insert(apArticleContent);

        return ResponseResult.okResult(apArticle.getId());//返回文章的id
    }

    @Value("${file.oss.web-site}")
    String website;
    @Autowired
    ApArticleMapper apArticleMapper;
    @Override
    public ResponseResult load(Short loadType, ArticleHomeDto dto) {
        //检查参数
        Integer size = dto.getSize();
        if (size==null||size<1){
            dto.setSize(10);
        }

        if (dto.getMaxBehotTime()==null){
            dto.setMaxBehotTime(new Date());
        }

        if (dto.getMinBehotTime()==null){
            dto.setMinBehotTime(new Date());
        }

        if (StringUtils.isBlank(dto.getTag())){
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        //调用mapper方法进行查询
        List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadType);

        //封装查询结果
        ResponseResult responseResult = ResponseResult.okResult(apArticles);
        responseResult.setHost(website);
        //不需要设置分页参数
        return responseResult;
    }

    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
        if (firstPage){
            //从redis缓存中查询   热点文章数据
            String hotArticleVoListJson = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if (StringUtils.isNotBlank(hotArticleVoListJson)){
                //转化为java操作的数组
                List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticleVoListJson, HotArticleVo.class);
                ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
                responseResult.setHost(website);
                return responseResult;
            }
        }
        return load(loadtypeLoadMore,dto);
    }

    @Override//查看文章详情
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        //dto通过请求体传,需要封装成dto    id
        //检查参数
        if (dto.getArticleId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章id不能为空");
        }
        //先看配置表  是否上架   是否已删除
        ApArticleConfig apArticleConfig = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, dto.getArticleId()));
        if (apArticleConfig==null||apArticleConfig.getIsDown()||apArticleConfig.getIsDelete()){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"查询文章配置信息出错");
        }

        //查询文章内容
        ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getArticleId()));
        if (apArticleContent==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"没有查询到文章");
        }

        //封装查询的结果
        Map map=new HashMap<>();
        map.put("content",apArticleContent);
        map.put("config",apArticleConfig);

        return ResponseResult.okResult(map);
    }




    @Autowired
    ArticleFeign articleFeign;
    @Autowired
    UserFeign userFeign;
    @Autowired
    BehaviorFeign behaviorFeign;

    @Autowired
    ApCollectionMapper apCollectionMapper;

    //加载文章详情的行为内容
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        boolean isfollow=false,iscollection=false;
        //1. 检查参数
        if(dto.getArticleId() == null || dto.getAuthorId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2. 查询登录用户id  远程查询关注信息
        ApAuthor author = articleFeign.selectById(dto.getAuthorId());
        ApUser user = AppThreadLocalUtils.getUser();
        if(author != null&&user!=null){
            ApUserFollow userFollow = userFeign.findByUserIdAndFollowId(user.getId(), author.getUserId());
            if(userFollow!=null){
                isfollow=true;
            }
        }
        //3. 远程查询行为微服务  查询点赞 和 不喜欢的信息
        ApArticleRelationDto relationDto = new ApArticleRelationDto();
        relationDto.setArticleId(dto.getArticleId()); // 文章id
        if(user==null){
            relationDto.setEntryId(dto.getEquipmentId());
            relationDto.setType(ApBehaviorEntry.Type.EQUIPMENT.getCode()); // 0 用户   1 设备
        }else {
            relationDto.setEntryId(user.getId());
            relationDto.setType(ApBehaviorEntry.Type.USER.getCode()); // 0 用户   1 设备
        }
        // islike  isunlike
        Map result = behaviorFeign.findApArticleRelation(relationDto);
        Object entryId = result.get("entryId");
        if(entryId!=null){
            //4. 根据文章id 和 行为实体id在当前微服务中 查询是否收藏的信息
            ApCollection apCollection = apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
                    .eq(ApCollection::getEntryId, entryId)
                    .eq(ApCollection::getArticleId, dto.getArticleId()));
            if(apCollection!=null){
                iscollection = true;
            }
        }
        //5. 将所有信息封装成一个map集合返回前台
        result.put("isfollow",isfollow);
        result.put("iscollection",iscollection);
        return ResponseResult.okResult(result);


    }

    /**
     * 实时流处理后    重新计算文章的分值
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        //stream流处理时   原数据不会被修改   只会得到一个新的集合

        //根据文章id  查询文章数据
        ApArticle apArticle = this.getById(mess.getArticleId());

        //更新文章指定行为数据
        if (mess.getView()!=0){
            apArticle.setViews(apArticle.getViews()==null?(int)mess.getView():apArticle.getViews()+(int)mess.getView());
            //原来数据 +实时计算的数据  = 最新的数据
        }
        if(mess.getLike()!=0){
            apArticle.setLikes(apArticle.getLikes()==null? (int)mess.getLike() : apArticle.getLikes()+(int)mess.getLike());
        }
        if(mess.getComment()!=0){
            apArticle.setComment(apArticle.getComment()==null? (int)mess.getComment() : apArticle.getComment()+(int)mess.getComment());
        }
        if(mess.getCollect()!=0){
            apArticle.setCollection(apArticle.getCollection()==null? (int)mess.getCollect() : apArticle.getCollection()+(int)mess.getCollect());
        }

        this.updateById(apArticle);//更新文章中的行为数据

        //按照权重计算文章分值    今日整体权重   *3
        Integer score = computeScore(apArticle);
        score=score*3;//今日得分

        //判断当前文章  在所属的频道  能否排到热点文章  前30   替换  分值较低  热点文章   两个公用一个方法
        updateArticleCache(apArticle,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        //判断当前文章  在所有频道  能否  排到  热点文章  前30  替换 得分值较低的热点文章

        //新思路    可以先添加到热点文章   在取前30



    }

    @Autowired
    RedisTemplate<String,String> redisTemplate;
    /**
     * 更新热点文章缓存
     * @param apArticle  待更新的文章
     * @param score      该文章的热度分值
     * @param cacheKey     redis缓存的key
     */
    private void updateArticleCache(ApArticle apArticle, Integer score, String cacheKey) {

        //查询redis中缓存  的热点文章列表
        String hotArticleListJson = redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isBlank(hotArticleListJson)){
            log.error("缓存热点文章失败");
            return;//结束
        }

        List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticleListJson, HotArticleVo.class);
        //转化为集合来操作
        //查看当前文章是否在热点文章缓存中  ,如果存在  ,更新缓存中的分数
        boolean isHas=false;
        for (HotArticleVo hotArticleVo : hotArticleVoList) {
            if (hotArticleVo.getId().equals(apArticle.getId())){

                hotArticleVo.setScore(score);
                isHas=true;
                break;
            }
        }

        //如果不存在,将当前文章  封装成vo添加到 热点文章集合中

        if (!isHas){

            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(apArticle,hotArticleVo);
            hotArticleVo.setScore(score);
            hotArticleVoList.add(hotArticleVo);

        }

        //将最终的热点文章集合  按照得分   降序  排序   取前30条

        hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).limit(30).collect(Collectors.toList());

        //替代原有的

        //将新的30条数据重新 缓存到  redis中
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleVoList));

    }


    /**
     * 计算得分
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        // 阅读数量
        Integer views = apArticle.getViews();
        if(views!=null){
            score += views * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞数量
        Integer likes = apArticle.getLikes();
        if(likes!=null){
            score += likes * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论数量
        Integer comment = apArticle.getComment();
        if(comment!=null){
            score += comment * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏数量
        Integer collection = apArticle.getCollection();
        if(collection!=null){
            score += collection * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}